// Main.cc
// This is basically just a wrapper for PigMain()
// It sets up the thread pools, initializes & cleans up SSH keys, etc.

#include <stdlib.h>

#include <iostream>

#include "servers.h"
#include "thread.h"
#include "jobthread.h"
#include "sendthread.h"
#include "threadpool.h"
#include "pigasus-types.h"
#include "push.h"
#include "wait.h"

using namespace std;

// Create threadpools and associated mutexes.
ThreadPool *job_thread_pool;
pthread_mutex_t *mutex_job_thread_pool;

ThreadPool *send_thread_pool;
pthread_mutex_t *mutex_send_thread_pool;

ThreadPool *get_thread_pool;
pthread_mutex_t *mutex_get_thread_pool;

Servers *global_servers;

// We expect PigMain from the translator.
extern void PigMain(List &args);

/*
void PigMain(List &args) {
  cout << "Pig main!" << endl;
  Map m;
  m.Set("host", "localhost");
  m.Set("user", "dummy");
  m.Set("password", "dummypw");
  List my_servers = NewList(m);
  
  //my_servers.Append(Map("user", "dummy"));
  //my_servers.Append(Map("password", "dummypw"));
  
  global_servers->SetServers(my_servers);
  
  List common = NewList(File("foo.bar"));
  // As I recall, inputs is suppose to be a list of lists? --John
  
  List inputs = NewList(NewList(File("pig")))
                + NewList(NewList(File("pigasus-types.o")));
  
  // List inputs;
  // inputs.Append(NewList(File("pig")));
  // inputs.Append(NewList(File("pigasus-types.o")));
  List outputs;
  
  int id = Push("a", common, inputs, NULL);

  Wait(id);

  printf("******* Wait is working!\n");

  // While a thread is still executing, keep all the threadpools alive.
  while (true) {
    bool a = job_thread_pool->HasJobsRunning();
    bool b = send_thread_pool->HasJobsRunning();
    bool c = get_thread_pool->HasJobsRunning();

    if (!(a || b || c)) break;
    sleep(1);
  }
}
*/

int main(int argc, char **argv, char **envp) {
  FILE *p;
  char ignore[20];
  char *err;
  // Initialize global pointers.
  job_thread_pool = new ThreadPool(kJobThreadType);
  mutex_job_thread_pool = new pthread_mutex_t;
  pthread_mutex_init(mutex_job_thread_pool, NULL);

  send_thread_pool = new ThreadPool(kSendThreadType);
  mutex_send_thread_pool = new pthread_mutex_t;
  pthread_mutex_init(mutex_send_thread_pool, NULL);

  get_thread_pool = new ThreadPool(kGetThreadType);
  mutex_get_thread_pool = new pthread_mutex_t;
  pthread_mutex_init(mutex_get_thread_pool, NULL);

  // The post_thread_pool should allow infinite (i.e. INT_MAX) threads.
  // post_thread_pool = new ThreadPool(-1);
  
  global_servers = new Servers();
  
  // Start the threads. They won't actually do anything yet.
  pthread_mutex_lock(mutex_job_thread_pool);
  job_thread_pool->Start();
  pthread_mutex_unlock(mutex_job_thread_pool);

  pthread_mutex_lock(mutex_send_thread_pool);
  send_thread_pool->Start();
  pthread_mutex_unlock(mutex_send_thread_pool);

  pthread_mutex_lock(mutex_get_thread_pool);
  get_thread_pool->Start();
  pthread_mutex_unlock(mutex_get_thread_pool);

  // post_thread_pool->Start();
  
  // Prepare List args for PigMain. Don't skip the first arg (executible name). 
  List args;
  for (int i = 0; i < argc; ++i) {   
    args.Append(string(argv[i]));
  }
  
  // Generates files pig_rsa and pig_rsa.pub
  p = popen("./makekey.sh", "r");
  while (!feof(p)) err = fgets(ignore, sizeof(ignore), p);
  pclose(p);
  
  PigMain(args);

  // All thread pools are now empty, and we are ready to end the program.
  debug("All thread pools are now empty.\n");

  pthread_mutex_destroy(mutex_job_thread_pool);
  pthread_mutex_destroy(mutex_send_thread_pool);
  pthread_mutex_destroy(mutex_get_thread_pool);

  delete mutex_job_thread_pool;
  delete mutex_send_thread_pool;
  delete mutex_get_thread_pool;
  
  // Clean up ssh files. TODO: delete keys on the remote servers.
  p = popen("ssh-add -d pig_rsa", "r");
  while (!feof(p)) err = fgets(ignore, sizeof(ignore), p);
  pclose(p);
  
  p = popen("rm pig_rsa pig_rsa.pub", "r");
  while (!feof(p)) err = fgets(ignore, sizeof(ignore), p);
  pclose(p);

  cout << "Done!" << endl;
  return 0;
}
